package cn.wjzhang.core.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ReflectUtil;
import cn.wjzhang.core.beans.vo.NoModelWriteData;
import cn.wjzhang.core.beans.vo.SimpleWriteData;
import cn.wjzhang.core.config.ExcelWidthStyleStrategy;
import cn.wjzhang.core.constant.CommonConstant;
import cn.wjzhang.core.listener.ExcelListener;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * excel工具类
 * easyexcel使用的3.0.2版本，跟以前版本有很大区别，且不兼容1.x版本
 *
 * @author liangxn
 */
@Slf4j
public class EasyExcelUtil {

    /**
     * 不创建对象的导出
     * 示例：
     * NoModelWriteData n = new NoModelWriteData();
     * n.setFileName("造价咨询-项目信息");
     * n.setDataList(DictAspectUtil.list2DictJsonArray(list));
     * n.setHeadMap(EasyExcelUtil.noModleHead(new CcConsultancyInfoVo()));
     * n.setDataStrMap(EasyExcelUtil.noModleField(new CcConsultancyInfoVo()));
     *
     * @param data
     * @param response
     * @throws IOException
     */
    public void noModleWrite(@RequestBody NoModelWriteData data, HttpServletResponse response) throws IOException {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode(data.getFileName(), "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 输出excel
            EasyExcel.write(response.getOutputStream()).head(head(data.getHeadMap())).registerWriteHandler(new ExcelWidthStyleStrategy()).registerWriteHandler(ExcelWidthStyleStrategy.getStyleStrategy()).sheet(data.getFileName()).doWrite(dataList(data.getDataList(), data.getDataStrMap()));
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    /**
     * 创建对象导出
     * 构造
     * SimpleWriteData s = new SimpleWriteData();
     * s.setDataList(list);
     * s.setFileName("造价咨询-项目信息");
     *
     * @param data
     * @param clazz
     * @param response
     * @param <T>
     * @throws IOException
     */
    //创建对象的导出
    public <T> void simpleWrite(@RequestBody SimpleWriteData data, Class<T> clazz, HttpServletResponse response) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
//        response.setContentType("application/vnd.ms-excel");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(data.getFileName(), "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clazz).registerWriteHandler(new ExcelWidthStyleStrategy()).registerWriteHandler(ExcelWidthStyleStrategy.getStyleStrategy()).sheet(data.getFileName()).doWrite(data.getDataList());
    }

    //设置表头
    private List<List<String>> head(String[] headMap) {
        List<List<String>> list = new ArrayList<List<String>>();
        for (String head : headMap) {
            List<String> headList = new ArrayList<String>();
            headList.add(head);
            list.add(headList);
        }
        return list;
    }

    //设置导出的数据内容
    private List<List<String>> dataList(List<JSONObject> dataList, String[] dataStrMap) {
        //这里可用Object 单时间会出错，建议采用String类型
        List<List<String>> list = new ArrayList<List<String>>();

        // 匹配数据与表头
        dataList.forEach(entity -> {
            List<String> data = new ArrayList<String>();
            for (String s : dataStrMap) {
                if (entity.containsKey(s)) {
                    String cellValue = entity.getString(s);
                    if (StringUtils.isNotBlank(cellValue)) {
                        data.add(cellValue);
                    } else {
                        data.add("");
                    }
                }
            }
            list.add(data);
        });
        return list;
    }

    /**
     * 根据对象获取表头
     *
     * @param clazz 对象
     * @param <T>   对象
     * @return excel所有表头
     */
    public static <T> String[] noModleHead(T clazz) {
        List<String> headArray = new ArrayList<>();
        // 遍历属性
        for (Field field : ReflectUtil.getFields(clazz.getClass())) {
            if (field.getAnnotation(ExcelProperty.class) != null) {
                // 暂时考虑一层表头
                String head = field.getAnnotation(ExcelProperty.class).value()[0];
                // 后期有顺序要求再考虑
//              int index = field.getAnnotation(ExcelProperty.class).index();
//              int order = field.getAnnotation(ExcelProperty.class).order();
                headArray.add(head);
            }
        }
        // 转为数组
        return headArray.toArray(new String[0]);
    }

    /**
     * 根据对象获取属性
     *
     * @param clazz 对象
     * @param <T>   对象
     * @return excel所有属性
     */
    public static <T> String[] noModleField(T clazz) {
        List<String> fieldArray = new ArrayList<>();
        // 遍历属性
        for (Field field : ReflectUtil.getFields(clazz.getClass())) {
            if (field.getAnnotation(ExcelProperty.class) != null) {
                // 暂时考虑一层表头
                String fieldName = field.getName();
                // 有Dict 对应的是数据字典  TODO
//                if (field.getAnnotation(Dict.class) != null) {
//                     数据字典属性加dictText
//                    fieldArray.add(fieldName + CommonConstant.DICT_TEXT_SUFFIX);
//                } else {
                    fieldArray.add(fieldName);
//                }
            }
        }
        // 转为数组
        return fieldArray.toArray(new String[0]);
    }

    /**
     * 读取Excel（一个sheet）
     *
     * @param excel   文件
     * @param clazz   实体类
     * @param sheetNo sheet序号
     * @return 返回实体列表(需转换)
     */
    public static <T> List<T> readExcel(MultipartFile excel, Class<T> clazz, int sheetNo) {

        ExcelListener excelListener = new ExcelListener();
        //
        ExcelReader excelReader = getReader(excel, clazz, excelListener);
        if (excelReader == null) {
            return new ArrayList<>();
        }
        // 读取sheet
        ReadSheet readSheet = EasyExcel.readSheet(sheetNo).build();
        excelReader.read(readSheet);
        excelReader.finish();
        // 转为对象
        return BeanUtil.copyToList(excelListener.getDataList(), clazz);
    }

    /**
     * 返回ExcelReader
     *
     * @param excel         文件
     * @param clazz         实体类
     * @param excelListener
     */
    private static <T> ExcelReader getReader(MultipartFile excel, Class<T> clazz, ExcelListener excelListener) {
        // 文件名
        String filename = excel.getOriginalFilename();

        try {
            if (StringUtils.isBlank(filename) || (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {
                return null;
            }
            // 读取文件流
            InputStream inputStream = new BufferedInputStream(excel.getInputStream());
            ExcelReader excelReader = EasyExcel.read(inputStream, clazz, excelListener).build();
            inputStream.close();
            return excelReader;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}